"bufstream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crates-io 0.2.0",
"crossbeam 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
- "curl-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.78 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"fs2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "git2-curl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "git2-curl 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hamcrest 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
name = "crates-io"
version = "0.2.0"
dependencies = [
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curl"
-version = "0.2.19"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "curl-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curl-sys"
-version = "0.1.34"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
[[package]]
name = "git2-curl"
-version = "0.4.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "curl 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curl 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
use std::fs::{self, File};
use std::iter::repeat;
use std::path::{Path, PathBuf};
+use std::time::Duration;
-use curl::http;
+use curl::easy::Easy;
use git2;
use registry::{Registry, NewCrate, NewCrateDependency};
use term::color::BLACK;
}
/// Create a new HTTP handle with appropriate global configuration for cargo.
-pub fn http_handle(config: &Config) -> CargoResult<http::Handle> {
+pub fn http_handle(config: &Config) -> CargoResult<Easy> {
// The timeout option for libcurl by default times out the entire transfer,
// but we probably don't want this. Instead we only set timeouts for the
// connect phase as well as a "low speed" timeout so if we don't receive
// many bytes in a large-ish period of time then we time out.
- let handle = http::handle().timeout(0)
- .connect_timeout(30_000 /* milliseconds */)
- .low_speed_limit(10 /* bytes per second */)
- .low_speed_timeout(30 /* seconds */);
- let handle = match try!(http_proxy(config)) {
- Some(proxy) => handle.proxy(proxy),
- None => handle,
- };
- let handle = match try!(http_timeout(config)) {
- Some(timeout) => handle.connect_timeout(timeout as usize)
- .low_speed_timeout((timeout as usize) / 1000),
- None => handle,
- };
+ let mut handle = Easy::new();
+ try!(handle.connect_timeout(Duration::new(30, 0)));
+ try!(handle.low_speed_limit(10 /* bytes per second */));
+ try!(handle.low_speed_time(Duration::new(30, 0)));
+ if let Some(proxy) = try!(http_proxy(config)) {
+ try!(handle.proxy(&proxy));
+ }
+ if let Some(timeout) = try!(http_timeout(config)) {
+ try!(handle.connect_timeout(Duration::new(timeout as u64, 0)));
+ try!(handle.low_speed_time(Duration::new(timeout as u64, 0)));
+ }
Ok(handle)
}
use std::io::prelude::*;
use std::path::{PathBuf, Path};
-use curl::http;
+use curl::easy::Easy;
use flate2::read::GzDecoder;
use git2;
use rustc_serialize::hex::ToHex;
cache_path: Filesystem,
src_path: Filesystem,
config: &'cfg Config,
- handle: Option<http::Handle>,
+ handle: Option<Easy>,
hashes: HashMap<(String, String), String>, // (name, vers) => cksum
cache: HashMap<String, Vec<(Summary, bool)>>,
updated: bool,
self.handle.as_mut().unwrap()
}
};
- // TODO: don't download into memory (curl-rust doesn't expose it)
- let resp = try!(handle.get(url.to_string()).follow_redirects(true).exec());
- if resp.get_code() != 200 && resp.get_code() != 0 {
- return Err(internal(format!("failed to get 200 response from {}\n{}",
- url, resp)))
+ // TODO: don't download into memory, but ensure that if we ctrl-c a
+ // download we should resume either from the start or the middle
+ // on the next time
+ try!(handle.get(true));
+ try!(handle.url(&url.to_string()));
+ try!(handle.follow_location(true));
+ let mut state = Sha256::new();
+ let mut body = Vec::new();
+ {
+ let mut handle = handle.transfer();
+ try!(handle.write_function(|buf| {
+ state.update(buf);
+ body.extend_from_slice(buf);
+ Ok(buf.len())
+ }));
+ try!(handle.perform());
+ }
+ let code = try!(handle.response_code());
+ if code != 200 && code != 0 {
+ bail!("failed to get 200 response from `{}`, got {}", url, code)
}
// Verify what we just downloaded
- let actual = {
- let mut state = Sha256::new();
- state.update(resp.get_body());
- state.finish()
- };
- if actual.to_hex() != expected_hash {
+ if state.finish().to_hex() != expected_hash {
bail!("failed to verify the checksum of `{}`", pkg)
}
- try!(dst.write_all(resp.get_body()));
+ try!(dst.write_all(&body));
try!(dst.seek(SeekFrom::Start(0)));
Ok(dst)
}
use std::io::{self, Cursor};
use std::result;
-use curl::http;
-use curl::http::handle::Method::{Put, Get, Delete};
-use curl::http::handle::{Method, Request};
+use curl::easy::{Easy, List};
use rustc_serialize::json;
use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET};
pub struct Registry {
host: String,
token: Option<String>,
- handle: http::Handle,
+ handle: Easy,
}
pub type Result<T> = result::Result<T, Error>;
}
pub enum Error {
- Curl(curl::ErrCode),
- NotOkResponse(http::Response),
+ Curl(curl::Error),
+ NotOkResponse(u32, Vec<String>, Vec<u8>),
NonUtf8Body,
Api(Vec<String>),
Unauthorized,
}
}
+impl From<curl::Error> for Error {
+ fn from(err: curl::Error) -> Error {
+ Error::Curl(err)
+ }
+}
+
#[derive(RustcDecodable)]
pub struct Crate {
pub name: String,
impl Registry {
pub fn new(host: String, token: Option<String>) -> Registry {
- Registry::new_handle(host, token, http::Handle::new())
+ Registry::new_handle(host, token, Easy::new())
}
- pub fn new_handle(host: String, token: Option<String>,
- handle: http::Handle) -> Registry {
+ pub fn new_handle(host: String,
+ token: Option<String>,
+ handle: Easy) -> Registry {
Registry {
host: host,
token: token,
Some(s) => s,
None => return Err(Error::TokenMissing),
};
- let request = self.handle.put(url, &mut body)
- .content_length(size)
- .header("Accept", "application/json")
- .header("Authorization", &token);
- let response = handle(request.exec());
- let _body = try!(response);
+ try!(self.handle.put(true));
+ try!(self.handle.url(&url));
+ try!(self.handle.in_filesize(size as u64));
+ let mut headers = List::new();
+ try!(headers.append("Accept: application/json"));
+ try!(headers.append(&format!("Authorization: {}", token)));
+ try!(self.handle.http_headers(headers));
+
+ let _body = try!(handle(&mut self.handle, &mut |buf| {
+ body.read(buf).unwrap_or(0)
+ }));
Ok(())
}
let formated_query = percent_encode(query.as_bytes(), QUERY_ENCODE_SET);
let body = try!(self.req(
format!("/crates?q={}&per_page={}", formated_query, limit),
- None, Get, Auth::Unauthorized
+ None, Auth::Unauthorized
));
let crates = try!(json::decode::<Crates>(&body));
}
fn put(&mut self, path: String, b: &[u8]) -> Result<String> {
- self.req(path, Some(b), Put, Auth::Authorized)
+ try!(self.handle.put(true));
+ self.req(path, Some(b), Auth::Authorized)
}
fn get(&mut self, path: String) -> Result<String> {
- self.req(path, None, Get, Auth::Authorized)
+ try!(self.handle.get(true));
+ self.req(path, None, Auth::Authorized)
}
fn delete(&mut self, path: String, b: Option<&[u8]>) -> Result<String> {
- self.req(path, b, Delete, Auth::Authorized)
+ try!(self.handle.custom_request("DELETE"));
+ self.req(path, b, Auth::Authorized)
}
- fn req(&mut self, path: String, body: Option<&[u8]>,
- method: Method, authorized: Auth) -> Result<String> {
- let mut req = Request::new(&mut self.handle, method)
- .uri(format!("{}/api/v1{}", self.host, path))
- .header("Accept", "application/json")
- .content_type("application/json");
+ fn req(&mut self,
+ path: String,
+ body: Option<&[u8]>,
+ authorized: Auth) -> Result<String> {
+ try!(self.handle.url(&format!("{}/api/v1{}", self.host, path)));
+ let mut headers = List::new();
+ try!(headers.append("Accept: application/json"));
+ try!(headers.append("Content-Type: application/json"));
if authorized == Auth::Authorized {
let token = match self.token.as_ref() {
Some(s) => s,
None => return Err(Error::TokenMissing),
};
- req = req.header("Authorization", &token);
+ try!(headers.append(&format!("Authorization: {}", token)));
}
+ try!(self.handle.http_headers(headers));
match body {
- Some(b) => req = req.body(b),
- None => {}
+ Some(mut body) => {
+ try!(self.handle.upload(true));
+ try!(self.handle.in_filesize(body.len() as u64));
+ handle(&mut self.handle, &mut |buf| body.read(buf).unwrap_or(0))
+ }
+ None => handle(&mut self.handle, &mut |_| 0),
}
- handle(req.exec())
}
}
-fn handle(response: result::Result<http::Response, curl::ErrCode>)
- -> Result<String> {
- let response = try!(response.map_err(Error::Curl));
- match response.get_code() {
+fn handle(handle: &mut Easy,
+ read: &mut FnMut(&mut [u8]) -> usize) -> Result<String> {
+ let mut headers = Vec::new();
+ let mut body = Vec::new();
+ {
+ let mut handle = handle.transfer();
+ try!(handle.read_function(|buf| Ok(read(buf))));
+ try!(handle.write_function(|data| {
+ body.extend_from_slice(data);
+ Ok(data.len())
+ }));
+ try!(handle.header_function(|data| {
+ headers.push(String::from_utf8_lossy(data).into_owned());
+ true
+ }));
+ try!(handle.perform());
+ }
+
+ match try!(handle.response_code()) {
0 => {} // file upload url sometimes
200 => {}
403 => return Err(Error::Unauthorized),
404 => return Err(Error::NotFound),
- _ => return Err(Error::NotOkResponse(response))
+ code => return Err(Error::NotOkResponse(code, headers, body))
}
- let body = match String::from_utf8(response.move_body()) {
+ let body = match String::from_utf8(body) {
Ok(body) => body,
Err(..) => return Err(Error::NonUtf8Body),
};
match *self {
Error::NonUtf8Body => write!(f, "response body was not utf-8"),
Error::Curl(ref err) => write!(f, "http error: {}", err),
- Error::NotOkResponse(ref resp) => {
- write!(f, "failed to get a 200 OK response: {}", resp)
+ Error::NotOkResponse(code, ref headers, ref body) => {
+ try!(writeln!(f, "failed to get a 200 OK response, got {}", code));
+ try!(writeln!(f, "headers:"));
+ for header in headers {
+ try!(writeln!(f, " {}", header));
+ }
+ try!(writeln!(f, "body:"));
+ try!(writeln!(f, "{}", String::from_utf8_lossy(body)));
+ Ok(())
}
Error::Api(ref errs) => {
write!(f, "api errors: {}", errs.join(", "))